package com.mf.service.impl.calculate;

import com.mf.bo.DateScopedBo;
import com.mf.entity.Code;
import com.mf.entity.CodeHistory;
import com.mf.entity.PurchaseRecord;
import com.mf.service.*;
import com.mf.service.CalculateService.Calculate4Service;
import com.mf.service.purchase.PurchaseCalculation;
import com.mf.utils.StockGatherUtils;
import com.mf.vo.CapitalVo;
import com.mf.vo.StockGatherVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;

/**
 * @Author: lmf
 * @Create: 2024/9/9 20:35
 * Description:
 */
@Slf4j
@Service
public class Calculate4ServiceImpl implements Calculate4Service {
    @Resource
    private IPurchaseRecordService purchaseRecordService;

    @Resource
    private ICodeHistoryService codeHistoryService;

    @Resource
    private PurchaseCalculation purchaseCalculation;


    @Resource
    private ICodeService codeService;
    // 总收益
    private double totalProfit = 0;
    // 盈利次数
    private int winCount = 0;
    // 失败次数
    private int failCount = 0;
    // 最小天数
    private int minDay = 500;
    // 本金
    private BigDecimal capital = new BigDecimal("100");

    // 卖出类型
    private int sellType1 = 0;
    private int sellType2 = 0;

    // 总天数
    private int totalDay = 0;

    @Override
    public List<CapitalVo> calculateSellDate() {
        return purchaseCalculation.calculateByBuyDate();
    }

    /**
     * 获取股票集合最小年第一天日期
     */
    public  int getMinYearFirstDay(List<CodeHistory> dayHistoryList, String year) {
        int _year = Integer.parseInt(year.substring(0, 4));
        if (dayHistoryList.get(0).getD().substring(0, 4).equals("" + _year)) {
            _year++;
        }
        for (int i = 0; i < dayHistoryList.size(); i++) {
            if (dayHistoryList.get(i).getD().substring(0, 4).equals(_year + "")) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 单个计算
     */
    @Override
    public void singleCalculate(String dm) {

        // 查询的年数
        final int year = 6;
        final int startYear = year - 2;

        final DateScopedBo dateScopedBo = new DateScopedBo();
        dateScopedBo.setStartDate(LocalDate.now().minusYears(year).toString());
        dateScopedBo.setEndDate(LocalDate.now().toString());


        final List<CodeHistory> dayHistoryList = codeHistoryService.getHistoryList(dateScopedBo, dm, "dn");
        if (dayHistoryList == null || dayHistoryList.isEmpty()) {
            return;
        }

        // 最小年份为当前查询天的前4年开始
        final int minYearFirstDay = getMinYearFirstDay(dayHistoryList, LocalDate.now().minusYears(startYear).toString());
        if (minYearFirstDay == -1) {
            return;
        }
        this.minDay = minYearFirstDay;


        if (dayHistoryList.size() > this.minDay) {
            final StockGatherVo dayStockGatherVo = StockGatherUtils.getStockGather(dayHistoryList);
            final List<CodeHistory> yearHistoryList = codeHistoryService.getHistoryList(dateScopedBo, dm, "yn");
            final StockGatherVo yearStockGatherVo = StockGatherUtils.getStockGather(yearHistoryList);


            int length = dayStockGatherVo.getDate().length;


            for (int i = this.minDay; i < length; i++) {

                final StringBuilder stringBuilder = new StringBuilder();
                // 基准买入价格
                double buyPrice = calculateByBuyPrice(yearStockGatherVo, dayStockGatherVo.getDate()[i]);


                // 获取当前年份的最低价
                final double price = calculateByThatTimePrice(dayStockGatherVo, dayStockGatherVo.getDate()[i]);

                // 如果今年是最低价，那么直接使用今年的价格
                if (buyPrice > price) {
                    buyPrice = price;
                }

                // 没有指导的最低价直接跳过
                if (buyPrice == 0) {
                    continue;
                }

                // 获取当前最低价格
                final double presentPrice = dayStockGatherVo.getK()[i][2];

                final double v = calculatePercentageChange(buyPrice, presentPrice);

                // 买入距离最低价不能高出10%
                if (v <= 10) {
                    // k[0] 开盘价   k[1] 收盘价  k[2] 最低价  k[3] 最高价1
                    // 前一天的开盘价和收盘价不能超过5%
                    boolean flag = (calculatePercentageChange(dayStockGatherVo.getK()[i - 1][0],
                            dayStockGatherVo.getK()[i - 1][1]) >= 5);
                    // 如果超过百分之5那么记录直到Ma10大于Ma5
                    if (flag) {
                        if (dayStockGatherVo.getMa5().get(i) < dayStockGatherVo.getMa10().get(i)) {
                            flag = false;
                        }
                    }

                    // 查询当前价格是否是否符合MA策略
                    if (calculateByMA(dayStockGatherVo, i, 3) && !flag) {
                        // 获取字符串当前日期
                        final String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
//                        if (date.equals(dayStockGatherVo.getDate()[i])) {
//                            final CanPurchaseDm purchaseDm = new CanPurchaseDm();
//                            purchaseDm.setDm(dayStockGatherVo.getDm());
//                            purchaseDm.setDate(dayStockGatherVo.getDate()[i]);
//                            canPurchaseDmService.save(purchaseDm);
//                        }
                        // 买入下标
                        int buyIndex = i + 1;


                        if (buyIndex < length) {
                            System.out.println("计算的买入基准价格" + buyPrice);
                            // 当天买入
                            stringBuilder.append(dm).append(": 符合买入策略，买入价格:").append(dayStockGatherVo.getK()[buyIndex][0]);
                            stringBuilder.append("买入日期").append(dayStockGatherVo.getDate()[buyIndex]);


                            // 卖出下标
                            int sellIndex;
                            sellIndex = calculateBySellPrice(dayStockGatherVo, buyIndex, stringBuilder, true);
                            if (sellIndex != -1) {
                                i = sellIndex + 1;
                                log.info("======>>>>持仓天数:{}", sellIndex - buyIndex);
                                this.totalDay += sellIndex - buyIndex;
                                log.info("stringBuilder:{}", stringBuilder);
                            } else {
                                log.info("最新买入暂时没有卖出日期");
                            }
                        }
                    }
                }

            }

            log.info("总收益:{}", this.totalProfit);
            log.info("盈利次数:{}", this.winCount);
            log.info("失败次数:{}", this.failCount);
            log.info("本金:{}", this.capital);
            log.info("卖出类型1:{}", this.sellType1);
            log.info("卖出类型2:{}", this.sellType2);
            log.info("总天数：{}", this.totalDay);

        }

    }


    @Override
    public void calculate(String dm) {

        final List<Code> codeList = codeService.findAll();
        boolean flag = false;
        for (Code code : codeList) {
            log.info("进入:{}", dm);

            if (!Objects.equals(dm, "") && !flag) {
                if (dm.equals(code.getDm())) {
                    flag = true;
                } else {
                    continue;
                }
            }
            log.info("开始计算:{}", code.getDm());

            // 睡眠1秒
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            singleCalculate(code.getDm());
        }


    }

    /**
     * 计算获取当年最低价
     */
    public double calculateByThatTimePrice(StockGatherVo stockGatherVo, String presentDate) {
        double price = 99999;
        String year = presentDate.substring(0, 4);
        for (int i = this.minDay; i < stockGatherVo.getDate().length; i++) {
//            log.info("i-《minDay:{}", stockGatherVo.getDate()[this.minDay]);
            if (stockGatherVo.getDate()[i].substring(0, 4).equals(year)) {
                if (price > stockGatherVo.getK()[i][2]) {
//                    log.info("stockGatherVo.getDate()[i]:{}", stockGatherVo.getDate()[i]);
                    price = stockGatherVo.getK()[i][2];
                }
            }
        }
        return price;
    }

    /**
     * 计算卖出时机
     */
    public int calculateBySellPrice(StockGatherVo stockGatherVo, int buyIndex, StringBuilder stringBuilder, boolean flag) {
        // 买入价格
        double buyPrice = stockGatherVo.getK()[buyIndex][0];

        // 记录买入后最高收入价格
        double maxPrice = 0;
        // 卖出下标
        int sellIndex = -1;

        // 计划卖出最低价
        double sellPrice = 0;

        for (int j = buyIndex + 1; j < stockGatherVo.getDate().length - 1; j++) {
            // k[0] 开盘价   k[1] 收盘价  k[2] 最低价  k[3] 最高价1
            maxPrice = stockGatherVo.getK()[j][1];

            if (isFallExceedsThreshold(buyPrice, maxPrice, -10)) {
                System.out.println("现对于买入后最高收盘价跌幅大于10%直接卖出");
                sellIndex = j;
                this.sellType1++;
                break;
            }

            // MA5小于MA10  这时记录准备卖出
            if (stockGatherVo.getMa5().get(j) < stockGatherVo.getMa10().get(j) && sellPrice == 0) {
                sellPrice = stockGatherVo.getK()[j][2];
            }

            if (sellPrice != 0) {
                if (stockGatherVo.getK()[j][1] < sellPrice) {
                    sellIndex = j;
                    break;
                }

                // 如果重新MA5大于MA10，则重置卖出价格
                if (stockGatherVo.getMa5().get(j) > stockGatherVo.getMa10().get(j)) {
                    sellPrice = 0;
                }
            }

        }
        if (sellIndex != -1 && flag) {
            final PurchaseRecord purchaseRecord = new PurchaseRecord();
            purchaseRecord.setBuyDate(stockGatherVo.getDate()[buyIndex]);
            purchaseRecord.setBuyPrice(buyPrice);
            purchaseRecord.setDm(stockGatherVo.getDm());
            purchaseRecord.setSellDate(stockGatherVo.getDate()[sellIndex]);
            purchaseRecord.setDays(sellIndex - buyIndex);
            purchaseRecord.setType(1);
            jointSellInfo(stockGatherVo, sellIndex, stringBuilder, buyPrice, purchaseRecord);
        }
        return sellIndex;
    }

    /**
     * 拼接卖出信息 , double maxPrice
     */
    public void jointSellInfo(StockGatherVo stockGatherVo, int sellIndex, StringBuilder stringBuilder, double buyPrice, PurchaseRecord purchaseRecord) {
        stringBuilder.append("卖出日期:").append(stockGatherVo.getDate()[sellIndex]);
        // 卖出价格
        double sellPrice = stockGatherVo.getK()[sellIndex][0];
        stringBuilder.append("卖出价格:").append(sellPrice);
        final double total = calculatePercentageChange(buyPrice, sellPrice);
        this.totalProfit += total;
        if (total > 0) {
            this.winCount++;

        } else {
            this.failCount++;
        }
        // 将1转换为BigDecimal
        BigDecimal one = BigDecimal.ONE;

        // 将total除以100转换为BigDecimal计算，并且设置小数点后两位（根据需要调整）
        BigDecimal fraction = BigDecimal.valueOf(total).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
        // 加上1
        BigDecimal sum = one.add(fraction);
        // 计算新的资本值
        BigDecimal newCapital = this.capital.multiply(sum);
        // 去除小数部分
        this.capital = newCapital.setScale(0, RoundingMode.DOWN);
        // 打印结果（或者进行其他操作）

        purchaseRecord.setSellPrice(sellPrice);
        purchaseRecord.setProfit(total);
        purchaseRecordService.save(purchaseRecord);
        stringBuilder.append("收益:").append(total).append(";");
    }


    /**
     * 计算A值相对于B值多了或少了百分之多少。
     *
     * @param a A值
     * @param b B值
     * @return 返回一个字符串表示A值相对于B值多了或少了百分之多少
     * 例如 a 5  b 10 返回 50
     */
    public double calculatePercentageChange(double a, double b) {
        // 防止除以零的情况
        if (b == 0) {
            throw new IllegalArgumentException("B值不能为零");
        }

        // 计算差值的百分比
        return ((b - a) / b) * 100;
    }

    /**
     * 判断A值相对于B值的跌幅是否超过指定的百分比。
     *
     * @param a         A值
     * @param b         B值
     * @param threshold 跌幅阈值百分比（例如：5表示5%）
     * @return 如果A值相对于B值的跌幅超过threshold，则返回true；否则返回false。
     */
    public boolean isFallExceedsThreshold(double a, double b, double threshold) {
        // 防止除以零的情况
        if (b == 0) {
            throw new IllegalArgumentException("B值不能为零");
        }
        // 比较跌幅百分比是否超过阈值
        return calculatePercentageChange(a, b) <= threshold;
    }


    /**
     * MA买入策略计算
     */
    public boolean calculateByMA(StockGatherVo stockGatherVo, int i, int dayCount) {
        int count = 0;
        for (int j = i; j > i - dayCount; j--) {
            // ma5 大于Ma 10
            boolean a1 = stockGatherVo.getMa5().get(i) > stockGatherVo.getMa10().get(j);
            // ma10 大于 前3天的Ma10
            boolean b1 = stockGatherVo.getMa10().get(i) > stockGatherVo.getMa10().get(j);
            // ma5 大于 前3天的Ma5
            boolean b2 = stockGatherVo.getMa5().get(i) > stockGatherVo.getMa5().get(j);
            if (a1 && b1 && b2) {
                count++;
            }
        }


        return count >= dayCount - 1;
    }

    /**
     * 根据底价集合计算合适买入的底价
     */
    public double calculateByBuyPrice(StockGatherVo stockGatherVo, String presentDate) {

        // 最终的价格
        double price = 0;

        // 底价落在最多的价格区间次数
        int maxCount = 0;
        for (int i = 1; i < stockGatherVo.getDate().length; i++) {
            // 当前日期在当前年份之后去除
            if (!isNotAfterCurrentYear(presentDate, stockGatherVo.getDate()[i])) {
//                log.info(stockGatherVo.getDate()[i]);
                break;
            }
//            log.info(stockGatherVo.getDate()[i]);

            // 当前底价落在最多的价格区间次数
            int count = 0;

            // k[0] 开盘价   k[1] 收盘价  k[2] 最低价  k[3] 最高价1
            final double[] price_i = stockGatherVo.getK()[i];
            for (int j = 1; j < stockGatherVo.getDate().length; j++) {

                final double[] price_j = stockGatherVo.getK()[j];
                final boolean withinFivePercent = isWithinFivePercent(price_j[2], price_i[2]);
                // 在区间之类记录
                if (withinFivePercent) {
                    count++;
                }
            }
            if (count > maxCount) {
                maxCount = count;
                price = price_i[2];
            }
        }
        return price;
    }

    /**
     * 判断A值是否在B值的正负5%范围内。
     *
     * @param a A值
     * @param b B值
     * @return 如果A值在B值的正负5%范围内，则返回true；否则返回false。
     */
    public static boolean isWithinFivePercent(double a, double b) {
        // 计算5%的范围
        double fivePercentRange = b * 0.03;

        // 计算B值的上下限
        double lowerBound = b - fivePercentRange;
        double upperBound = b + fivePercentRange;

        // 检查A值是否在B值的5%范围内
        return a >= lowerBound && a <= upperBound;
    }

    /**
     * 判断 presentDate 是否大于等于 endDate
     *
     * @return 如果日期字符串不大于当前年份，则返回true；否则返回false。
     */
    public static boolean isNotAfterCurrentYear(String presentDate, String endDate) {
        try {
            int p = Integer.parseInt(presentDate.substring(0, 4));
            int e = Integer.parseInt(endDate.substring(0, 4));

            // 比较日期
            return p > e;
        } catch (DateTimeParseException e) {
            // 处理日期解析错误的情况
            System.err.println("Invalid date format: " + endDate);
            return false;
        }
    }
}
